﻿using System.Net;
using System.Text;
using Api.Attributes;
using Microsoft.AspNetCore.Http.Features;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Api.Middleware
{
    /// <summary>
    /// 全局统一返回体 + 全局统一异常处理
    /// </summary>
    public class ResponseBodyMiddleware
    {
        #region Ioc
        private readonly RequestDelegate _next;

        public ResponseBodyMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        #endregion

        #region InvokeAsync
        public async Task InvokeAsync(HttpContext context)
        {
            /**
             * 关于如何获取Response.Body的内容、如何解决异常时返回内容为空，参考下面
             * https://stackoverflow.com/questions/62588709/re-writing-response-body-asp-net-core-custome-middleware
             * https://www.coder.work/article/6402977
             * 
             * 关于修改响应内容，原本的代码在返回【decimal?】类型的时候，有时候会有问题，为什么有问题，不知道，在网上又找的方法，参考如下
             * 有问题，并不是接口报错或报异常，就是在返回的响应内容中无法正常序列化成json，在swagger中会直接告知不是json格式，总是会在结尾多一些不固定的字符
             * https://stackoverflow.com/questions/44508028/modify-middleware-response
             */

            var request = context.Request;
            var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;

            if (endpoint.IsNotEmpty() && endpoint.Metadata.GetMetadata<NotResponseBodyAttribute>().IsNotEmpty())
            {
                //如果有特性，则跳过此中间件
                await _next(context);
            }
            else if ((request.Path.ToString().ToLower().StartsWith("/upload/") || request.Path.ToString().ToLower().Contains("favicon.ico")) && request.Method.ToLower() == "get")
            {
                //如果是访问根目录下的Upload文件夹，则跳过此中间件
                //这里并不是说调用上传接口，而是通过浏览器访问Upload文件夹下的内容
                await _next(context);
            }
            else
            {
                object res = null;

                var response = context.Response;
                var originalBodyStream = response.Body;
                Stream stream = null;
                using (var memStream = new MemoryStream())
                {
                    try
                    {
                        response.Body = memStream;

                        await _next(context);

                        if (context.Response.StatusCode == (int)HttpStatusCode.OK)
                        {
                            stream = response.Body;
                            memStream.Seek(0, SeekOrigin.Begin);

                            using (var reader = new StreamReader(stream, leaveOpen: true))
                            {
                                string body = await reader.ReadToEndAsync();

                                //正常返回数据
                                res = new
                                {
                                    Code = 0,
                                    Msg = default(string),
                                    Data = body.Deserialize<object>() ?? body
                                };
                            }
                        }
                        else
                        {
                            stream = response.Body;
                            res = new
                            {
                                Code = 1,
                                Msg = $"服务端未处理响应状态 {context.Response.StatusCode} ！",
                                Data = default(object)
                            };
                        }
                    }
                    catch (CustomException ex)
                    {
                        stream = response.Body;

                        //自定义异常处理，一般是用于级客户端返回警告
                        res = new
                        {
                            Code = ex.Code,
                            Msg = ex.Message,
                            Data = default(object)
                        };
                    }
                    catch (Exception ex)
                    {
                        stream = response.Body;

                        //记录异常日志
                        Gx.Log(ex.ToString(), "系统异常-");

                        //返回系统中未处理的异常信息
                        res = new
                        {
                            Code = 500,
                            Msg = AppSetting.Config.SystemSwitch.IsShowException ? ex.ToString() : "服务端发生异常！",
                            Data = AppSetting.Config.SystemSwitch.IsShowException ? ex.Message : null
                        };
                    }


                    //设置返回内容格式
                    response.ContentType = "application/json; charset=utf-8";

                    //设置返回对象名称以驼峰规则
                    JsonSerializerSettings settings = new()
                    {
                        ContractResolver = new CamelCasePropertyNamesContractResolver()
                    };

                    var resString = JsonConvert.SerializeObject(res, settings);

                    stream.SetLength(0);
                    using (var writer = new StreamWriter(stream, leaveOpen: true))
                    {
                        await writer.WriteAsync(resString);
                        await writer.FlushAsync();
                        response.ContentLength = stream.Length;

                        memStream.Seek(0, SeekOrigin.Begin);
                        await memStream.CopyToAsync(originalBodyStream);
                        response.Body = originalBodyStream;
                    }
                }
            }
        }
        #endregion
    }

    #region 扩展方法
    public static class ResponseBodyMiddlewareExt
    {
        public static IApplicationBuilder UseResponseBody(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ResponseBodyMiddleware>();
        }
    }
    #endregion
}
